#include "FtpClient.h"
#include "FtpServer.h"
#include "FtpMessages.h"


#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define disconnect(s) close(s)

#include <errno.h>

// present in FtpClient.c
extern char* itoa(char* in, int val);

static char buffer[512];

void FtpClient_OnCmdQuit( FtpClient* pClient )
{
	assert( pClient );

	FtpClient_Send( pClient, 221, pClient->m_pMessages[FTPMSG_GOODBYE] );
	FtpServer_OnClientDisconnect(pClient->m_pServer,pClient);
}

void FtpClient_OnCmdUser( FtpClient* pClient, const char* pUser )
{
	Userlog* p;
	Userlog* temp = pClient->m_pServer->userList;

	assert( pClient );


	if(!strcmp(pUser,"anonymous") && pClient->m_pServer->m_iAnonymous )
	{
		// anonymous login, user authenticated
		pClient->m_eAuthState = AUTHSTATE_VALID;

		FtpClient_Send(pClient,230, pClient->m_pMessages[FTPMSG_USER_LOGGED_IN] );
	}
	else
	{
		p=pClient->m_pServer->userList;
		// allow password check

		pClient->m_eAuthState = AUTHSTATE_FAKEPASS;
		while(p!=NULL){
			if(!strcmp(pUser,p->m_Username)){
				pClient->m_eAuthState = AUTHSTATE_PASSWORD;
				break;
			}
			p=p->userlog_Next;
		}

		FtpClient_Send(pClient,331, pClient->m_pMessages[FTPMSG_PASSWORD_REQUIRED_FOR_USER] ); // after this line, username is destroyed
	}
	pClient->m_pServer->userList=temp;
}

void FtpClient_OnCmdPass( FtpClient* pClient, const char* pPass )
{

	Userlog* temp = pClient->m_pServer->userList;
	Userlog* p=temp;

	while (p != NULL) {
		if (!strcmp(pPass, p->m_Password)) {
			// password matches, allow login
			FtpClient_Send(pClient, 230, "User logged in.");
			pClient->m_eAuthState = AUTHSTATE_VALID;
			return;
		}
		p = p->userlog_Next;
	}

	if (pClient->m_eAuthState == AUTHSTATE_PASSWORD) {
		FtpClient_Send(pClient, 230, "User logged in.");
		pClient->m_eAuthState = AUTHSTATE_VALID;
	} else {
		// password didn't match, or no valid login
		if (AUTHSTATE_INVALID != pClient->m_eAuthState) {
			FtpClient_Send(pClient, 530,
					pClient->m_pMessages[FTPMSG_LOGIN_INCORRECT]);

			// disconnect client if more than 3 attempts to login has been made
			pClient->m_iAuthAttempt++;
			if (pClient->m_iAuthAttempt > 3)
				FtpServer_OnClientDisconnect(pClient->m_pServer, pClient);

			pClient->m_eAuthState = AUTHSTATE_INVALID;
		} else
			FtpClient_Send(pClient, 503,
					pClient->m_pMessages[FTPMSG_LOGIN_WITH_USER_FIRST]);
	}
	pClient->m_pServer->userList=temp;
}


void FtpClient_OnCmdPasv( FtpClient* pClient )
{
	struct sockaddr_in sa;
	socklen_t sl;
	int s;
	unsigned int addr;
	unsigned short port;
	char* buf;

	assert( pClient );

	FtpClient_OnDataCleanup(pClient);

	// get socket address will listen
	sl = sizeof(sa);
	if( getsockname( pClient->m_iControlSocket, (struct sockaddr*)&sa,&sl ) < 0 )
	{
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_ENTER_PASSIVE_MODE] );
		return;
	}
	sa.sin_port = 0;

	if( (s = socket( AF_INET, SOCK_STREAM, 0)) < 0 )
  {
  	disconnect(s);
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_ENTER_PASSIVE_MODE] );
		return;
	}

	if( bind( s, (struct sockaddr*)&sa, sl ) < 0 )
	{
  	disconnect(s);
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_ENTER_PASSIVE_MODE] );
		return;
	}

	if( listen( s, 1 ) < 0 )
  {
  	disconnect(s);
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_ENTER_PASSIVE_MODE] );
		return;
	}

	sl = sizeof(sa);
	if( getsockname( s, (struct sockaddr*)&sa,&sl ) < 0 )
	{
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_ENTER_PASSIVE_MODE] );
		return;
	}

	// store socket and flag us as listening
	pClient->m_iDataSocket = s;
	pClient->m_eConnState = CONNSTATE_LISTEN;

	addr = htonl(sa.sin_addr.s_addr);
	port = htons(sa.sin_port);

	buf = buffer;
	strcpy( buf, pClient->m_pMessages[FTPMSG_ENTERING_PASSIVE_MODE]);
	strcat(buf," (");
	itoa(buf+strlen(buf),(addr>>24)&0xff);
	strcat(buf,",");
	itoa(buf+strlen(buf),(addr>>16)&0xff);
	strcat(buf,",");
	itoa(buf+strlen(buf),(addr>>8)&0xff);
	strcat(buf,",");
	itoa(buf+strlen(buf),(addr)&0xff);
	strcat(buf,",");
	itoa(buf+strlen(buf),port>>8);
	strcat(buf,",");
	itoa(buf+strlen(buf),port&0xff);
	strcat(buf,").");

	FtpClient_Send( pClient, 227, buffer );
}

void FtpClient_OnCmdPort( FtpClient* pClient, int* ip, int port )
{
	assert( pClient );

	if( port >= 1024 )
	{
		pClient->m_iRemoteAddress[0] = ip[0];
		pClient->m_iRemoteAddress[1] = ip[1];
		pClient->m_iRemoteAddress[2] = ip[2];
		pClient->m_iRemoteAddress[3] = ip[3];

		pClient->m_iRemotePort = port;

		FtpClient_Send( pClient, 200, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
	}
	else
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_ILLEGAL_COMMAND] );
}

void FtpClient_OnCmdSyst( FtpClient* pClient )
{
	assert( pClient );
	FtpClient_Send( pClient, 215, "UNIX Type: L8" );
}

void FtpClient_OnCmdList( struct FtpClient* pClient, const char* pPath, int iNamesOnly )
{
	assert( pClient );

	if( iNamesOnly ){
		pClient->m_eDataAction = DATAACTION_NLST;
	}
	else{
		pClient->m_eDataAction = DATAACTION_LIST;
	}
	// attempt to open directory

	if( FileSystem_OpenDir(&pClient->m_kContext,pPath) < 0 )
	{
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_UNABLE_TO_OPEN_DIRECTORY] );
		return;
	}

	FtpClient_HandleDataConnect( pClient );
}

void FtpClient_OnCmdType( FtpClient* pClient, const char* pType )
{
	if( (tolower(*pType) == 'i')||(tolower(*pType) == 'a') )
		FtpClient_Send( pClient, 200, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
	else
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_ILLEGAL_COMMAND] );
}

void FtpClient_OnCmdMode( FtpClient* pClient, const char* pMode )
{
	// only stream-mode supported

	if( tolower(*pMode) == 's' )
		FtpClient_Send( pClient, 200, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
	else
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COMMAND_FAILED] );
}

void FtpClient_OnCmdRetr( FtpClient* pClient, const char* pFile )
{
	int iMarker = pClient->m_iRestartMarker;
	pClient->m_iRestartMarker = 0;

	if( FileSystem_OpenFile( &pClient->m_kContext, pFile, FM_READ, iMarker ) < 0 )
	{
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_FILE_NOT_FOUND] );
		return;
	}

	pClient->m_eDataAction = DATAACTION_RETR;

	FtpClient_HandleDataConnect( pClient );
}

void FtpClient_OnCmdStor( FtpClient* pClient, const char* pFile )
{
	int iMarker = pClient->m_iRestartMarker;
	pClient->m_iRestartMarker = 0;

	if( FileSystem_OpenFile( &pClient->m_kContext, pFile, FM_WRITE, iMarker ) < 0 )
	{
		FtpClient_Send( pClient, 500, pClient->m_pMessages[FTPMSG_COULD_NOT_CREATE_FILE] );
		return;
	}

	pClient->m_eDataAction = DATAACTION_STOR;

	FtpClient_HandleDataConnect( pClient );
}

void FtpClient_OnCmdRest( FtpClient* pClient, int iMarker )
{
	if( iMarker < 0 )
	{
		pClient->m_iRestartMarker = 0;
		FtpClient_Send( pClient, 501, pClient->m_pMessages[FTPMSG_INVALID_RESTART_MARKER] );
	}
	else
	{
		pClient->m_iRestartMarker = iMarker;
		FtpClient_Send( pClient, 350, pClient->m_pMessages[FTPMSG_RESTART_MARKER_SET] );
	}
}

void FtpClient_OnCmdPwd( FtpClient* pClient )
{
	char* buf2 = buffer;

	*(buf2++) = '\"';
	*(buf2) = '\0';
	strcat( buf2, pClient->m_kContext.m_Path );
	buf2 = buf2+strlen(buf2);
	*(buf2++) = '\"';
	*(buf2) = '\0';

	FtpClient_Send( pClient, 257, buffer );
}

void FtpClient_OnCmdCwd( FtpClient* pClient, const char* pPath )
{
	if( FileSystem_ChangeDir(&pClient->m_kContext,pPath) < 0 )
		FtpClient_Send( pClient, 550, pClient->m_pMessages[FTPMSG_COMMAND_FAILED] );
	else
		FtpClient_Send( pClient, 250, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
}

void FtpClient_OnCmdMkd( FtpClient* pClient, const char* pDir )
{
	if( FileSystem_CreateDir(&pClient->m_kContext,pDir) < 0 )
		FtpClient_Send( pClient, 550, pClient->m_pMessages[FTPMSG_COMMAND_FAILED] );
	else
		FtpClient_Send( pClient, 250, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
}

void FtpClient_OnCmdRmd( FtpClient* pClient, const char* pDir )
{
	if( FileSystem_DeleteDir(&pClient->m_kContext,pDir) < 0 )
		FtpClient_Send( pClient, 550, pClient->m_pMessages[FTPMSG_COMMAND_FAILED] );
	else
		FtpClient_Send( pClient, 250, pClient->m_pMessages[FTPMSG_COMMAND_SUCCESSFUL] );
}
